package view;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.border.BevelBorder;
import javax.swing.border.LineBorder;
import javax.swing.filechooser.FileFilter;

import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.annotation.*;

import constants.Constants;
import constants.Events;
import model.IModel;
//-----------------------|


/**
 * This code was edited or generated using CloudGarden's Jigloo
 * SWT/Swing GUI Builder, which is free for non-commercial
 * use. If Jigloo is being used commercially (ie, by a corporation,
 * company or business for any purpose whatever) then you
 * should purchase a license for each developer using Jigloo.
 * Please visit www.cloudgarden.com for details.
 * Use of Jigloo implies acceptance of these licensing terms.
 * A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR
 * THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED
 * LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE.
 */
public class View extends JFrame {
	private static final long serialVersionUID = 1L;

	//Menu-related variables
	private JMenuBar menuBar;
	private JMenu menu;
	private JMenu menuSound;
	private JMenu menuHelp;
	private JMenu menuSettings;
	private JMenuItem menuNewGame;
	private JMenuItem menuNetwork;
	private JMenuItem menuSave;
	private JMenuItem menuLoad;
	private AbstractAction rulesButtonAction;
	private JMenuItem rulesButton;
	private JLabel labelTime;
	private AbstractAction highScoreButtonAction;
	private AbstractAction actionThrow;
	private AbstractAction actionSave;
	private AbstractAction actionLoad;
	private AbstractAction actionMusic;
	private JMenuItem buttonViewHighScore;
	private JMenu menuHighScore;
	private JMenuItem exitButton;
	private JMenuItem menuItemShowHelp;
	private JMenuItem menuItemAbout;
	private JCheckBoxMenuItem menuMusic;
	private JCheckBoxMenuItem menuFX;
	private JSeparator menuSeparator1;
	private JSeparator menuSeparator2;
	private AbstractAction exitAction;
	private AbstractAction menuAboutAction;
	private AbstractAction menuHelpAction;
	private AbstractAction abstractNewGame;
	private AbstractAction playerButtonAction;
	private JMenuItem playerButton;
	private Help h = new Help();

	//Important gameplay related variables
	private JTextField txtDie; //Show the result of a die throw
	private JTextField txtTurn;//Who's turn is it?
	private JTextField txtTime;//Play your turn before the time runs out.

	//Other variables
	private DieButton btnDie;
	private JLabel lblDie;
	private JLabel lblTurn;
	private JLabel lblP4;
	private JLabel lblP3;
	private JLabel lblP2;
	private JLabel lblP1;
	private StartBox panelP3;
	private StartBox panelP2;
	private StartBox panelP4;
	private StartBox panelP1;

	private PlayerSettings pSettings;

	/**
	 * A set of arrays to hold pointers to View objects. Used
	 * for iteration during event handling.
	 */
	private JLabel[] playerNames;
	private StartBox[] startBoxes;

	public JLabel[] getPlayerNames() {
		return playerNames;

	}

	public View() {
		AnnotationProcessor.process(this);
		initGUI();	
		initSquares();

		StartBox[] startBoxes2 = {panelP1, panelP2, panelP3, panelP4};

		this.startBoxes = startBoxes2;
		pSettings = new PlayerSettings(this);
		this.setIconImage(getToolkit().getImage(Constants.GUI.main_window_icon));
		repaint();
	}	

	/**
	 * Initiate the View, which is the main graphical form for Ludo3k.
	 */
	private void initGUI() {
		try {
			{
				getContentPane().setLayout(null);
				getContentPane().setBackground(new java.awt.Color(255,255,255));
				this.setTitle(Constants.GUI.main_window_title);
				this.setResizable(false);
				this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				{
					menuBar = new JMenuBar();
					setJMenuBar(menuBar);
					{
						menu = new JMenu();
						menuBar.add(menu);
						menuBar.add(getMenuSettings());
						menuBar.add(getMenuHelp());
						menuBar.add(getMenuHighScore());
						menu.setText("File");
						{
							exitButton = new JMenuItem();	
							menu.add(getMenuNewGame());
							menu.add(getMenuNetwork());
							menu.add(getMenuSeparator2());
							menu.add(getMenuSave());
							menu.add(getMenuLoad());							
							menu.add(getMenuSeparator1());
							menu.add(exitButton);
							exitButton.setAction(getExitAction());
							exitButton.setText("Exit");	
							exitButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
						}
					}
				}
			}
			pack();
			this.setSize(Constants.GUI.main_window_width, Constants.GUI.main_window_height);
			this.setVisible(true);
			setLocationRelativeTo(null);
			this.setFont(new java.awt.Font("Tahoma",0,10));
			getContentPane().add(getBtnDie());
			getContentPane().add(getTxtTime());
			getContentPane().add(getLblDie());
			getContentPane().add(getTxtDie());
			getContentPane().add(getLblTurn());
			getContentPane().add(getTxtTurn());
			getContentPane().add(getPanelP1());
			getContentPane().add(getPanelP2());
			getContentPane().add(getPanelP3());
			getContentPane().add(getPanelP4());
			getContentPane().add(getLblP1());
			getContentPane().add(getLblP2());
			getContentPane().add(getLblP3());
			getContentPane().add(getLblP4());

		} catch(Exception e) {
			e.printStackTrace();
		}
		JLabel[] playerNames2  = {lblP1, lblP2, lblP3, lblP4};
		this.playerNames = playerNames2;
		repaint();
	}

	/**
	 * If the menu item "Settings" is not initiated, initiate it,
	 * and return a reference.
	 * @return menuSettings
	 */
	private JMenu getMenuSettings() {
		if(menuSettings == null) {
			menuSettings = new JMenu();
			menuSettings.setText("Settings");
			menuSettings.add(getMenuSound());
			menuSettings.add(getRulesButton());
			menuSettings.add(getPlayerButton());
		}
		return menuSettings;
	}

	/**
	 * If the menu item "Help" is not initiated, initiate it,
	 * and return a reference. Add two items to it: "About" 
	 * and "Show Help".
	 * @return menuHelp
	 */
	private JMenu getMenuHelp() {
		if(menuHelp == null) {
			menuHelp = new JMenu();
			menuHelp.setText("Help");
			menuHelp.add(getMenuItemAbout());
			menuHelp.add(getMenuItemShowHelp());
		}
		return menuHelp;
	}


	/**
	 * If the menu sub-item "Show Help" is not initiated, initiate it,
	 * and return a reference. 
	 * @return menuItemShowHelp
	 */
	private JMenuItem getMenuItemShowHelp() {
		if(menuItemShowHelp == null) {
			menuItemShowHelp = new JMenuItem();
			menuItemShowHelp.setText("Show Help");
			menuItemShowHelp.setAction(getMenuHelpAction());
		}
		return menuItemShowHelp;
	}

	/**
	 * If the menu sub-item "Save" is not initiated, initiate it,
	 * and return a reference.
	 * @return menuSave
	 */
	private JMenuItem getMenuSave() {
		if(menuSave == null) {
			menuSave = new JMenuItem();
			menuSave.setText("Save");
			menuSave.setAction(getActionSave());
			menuSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK));
		}
		return menuSave;
	}

	/**
	 * If the menu sub-item "Sound" is not initiated, initiate it,
	 * and return a reference. Also add two further sub-items to
	 * it; menuFX and menuMusic.
	 * @return menuSound
	 */
	private JMenu getMenuSound() {
		if(menuSound == null) {
			menuSound = new JMenu();
			menuSound.setText("Sound");
			menuSound.add(getMenuFX());
			menuSound.add(getMenuMusic());
		}
		return menuSound;
	}

	/**
	 * If the menu sub-item "menuFX" is not initiated, initiate it,
	 * and return a reference.
	 * @return menuFX
	 */
	private JCheckBoxMenuItem getMenuFX() {
		if(menuFX == null) {
			menuFX = new JCheckBoxMenuItem("FX",false);
			menuFX.setText("FX");
			menuFX.setAction(getAbstractActionFX());
		}
		return menuFX;
	}

	/**
	 * If the menu sub-item "menuMusic" is not initiated, initiate it,
	 * and return a reference.
	 * @return
	 */
	private JCheckBoxMenuItem getMenuMusic() {
		if(menuMusic == null) {
			menuMusic = new JCheckBoxMenuItem("Music", false);
			menuMusic.setText("Music");
		}
		return menuMusic;
	}


	/**
	 * If the menu sub-item "fxMusic" is not initiated, initiate it,
	 * and return a reference.
	 * @return
	 */
	@SuppressWarnings("serial")
	private AbstractAction getAbstractActionFX() {
		if(actionMusic == null) {
			actionMusic = new AbstractAction("FX", null) {

				public void actionPerformed(ActionEvent evt) {
					//Publish SFX true/false (toggle on/off)
						EventBus.publish(Events.SFX.toString(), menuFX.isSelected());
				}
			};
		}
		return actionMusic;
	}

	/**
	 * If the menu sub-item "Load" is not initiated, initiate it,
	 * and return a reference.
	 * @return menuLoad
	 */
	private JMenuItem getMenuLoad() {
		if(menuLoad == null) {
			menuLoad = new JMenuItem();
			menuLoad.setText("Load");
			menuLoad.setAction(getActionLoad());
			menuLoad.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.ALT_MASK));
		}
		return menuLoad;
	}

	/**
	 * If the menu sub-item "New Game" is not initiated, initiate it,
	 * and return a reference.
	 * @return menuNewGame
	 */
	private JMenuItem getMenuNewGame() {
		if(menuNewGame == null) {
			menuNewGame = new JMenuItem();
			menuNewGame.setText("New Game");
			menuNewGame.setAction(getAbstractNewGame());
			menuNewGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.ALT_MASK));
		}
		return menuNewGame;
	}

	/**
	 * If the menu sub-item "Network" is not initiated, initiate it,
	 * and return a reference.
	 * @return
	 */
	private JMenuItem getMenuNetwork() {
		if(menuNetwork == null) {
			menuNetwork = new JMenuItem();
			menuNetwork.setText("Network Game");
		}
		return menuNetwork;
	}

	/**
	 * If the menu sub-item "About" is not initiated, initiate it,
	 * and return a reference.
	 * @return
	 */
	private JMenuItem getMenuItemAbout() {
		if(menuItemAbout == null) {
			menuItemAbout = new JMenuItem();
			menuItemAbout.setText("jMenuItem1");
			menuItemAbout.setAction(getMenuAboutAction());
			menuItemAbout.setBounds(51, 19, 56, 19);
		}
		return menuItemAbout;
	}


	/**
	 * Associate an action with clicking the "About" option on the 
	 * About tab. 
	 * @return menuHelpAction
	 */
	private AbstractAction getMenuAboutAction() {
		if(menuAboutAction == null) {
			final View v = this;
			menuAboutAction = new AbstractAction("About Us", null) {
				private static final long serialVersionUID = 1L;
				public void actionPerformed(ActionEvent evt) {
					new About(v);
				}
			};
		}
		return menuAboutAction;
	}

	/**
	 * Associate an action with clicking the "Show Help" option on the 
	 * About tab. 
	 * @return menuHelpAction
	 */
	private AbstractAction getMenuHelpAction() {
		if(menuHelpAction == null) {
			menuHelpAction = new AbstractAction("Show Help", null) {
				private static final long serialVersionUID = 1L;
				public void actionPerformed(ActionEvent evt) {
					h.setVisible(true);
				}
			};
		}
		return menuHelpAction;
	}

	/**
	 * Associate an action with exiting the application
	 * @return exitAction
	 */
	private AbstractAction getExitAction() {
		if(exitAction == null) {
			exitAction = new AbstractAction("Exit", null) {
				private static final long serialVersionUID = 1L;
				public void actionPerformed(ActionEvent evt) {
					EventBus.publish(Events.END_GAME.toString(), null);
					disposeAll();
				}
			};
		}
		return exitAction;
	}

	/**
	 * Dispose of all Frames created by the application.
	 * This will terminate the application!
	 */
	private void disposeAll(){
		Frame[] allFrames = JFrame.getFrames();
		for(Frame f: allFrames)
			f.dispose();
	}

	/**
	 * Initiate the first menuSeparator if it is not initiated already
	 * @return menuSeparator1
	 */
	private JSeparator getMenuSeparator1() {
		if(menuSeparator1 == null) {
			menuSeparator1 = new JSeparator();
		}
		return menuSeparator1;
	}

	/**
	 * Initiate the second menuSeparator if it is not initialized already.
	 * @return menuSeparator2
	 */
	private JSeparator getMenuSeparator2() {
		if(menuSeparator2 == null) {
			menuSeparator2 = new JSeparator();
		}
		return menuSeparator2;
	}

	/**
	 * The button that throws the die.
	 * @return btnDie
	 */
	private JButton getBtnDie() {
		if(btnDie == null) {
			btnDie = new DieButton();
			btnDie.setText("Die");
			btnDie.setBounds(352, 270, 51, 51);            
			btnDie.setAction(getActionThrow());   
			btnDie.setToolTipText("Throw the Die!");
			btnDie.setMargin(new Insets(2,2,2,2));         
			btnDie.setIcon(new ImageIcon("img/die6.png"));
			btnDie.setFont(new java.awt.Font("Tahoma",0,11));
			btnDie.setPreferredSize(new java.awt.Dimension(65, 60));
		}
		return btnDie;
	}



	private AbstractAction getActionThrow() {
		if(actionThrow == null) {
			actionThrow = new AbstractAction("THROW", null) {
				private static final long serialVersionUID = 1L;

				public void actionPerformed(ActionEvent evt) {
					System.out.println(evt.getActionCommand());
					System.out.println(evt.getID());
					System.out.println(evt.getModifiers());
					System.out.println(evt.getSource());
					System.out.println(evt.getWhen());

					EventBus.publish(Events.DICETHROW.toString(), null);
				}
			};
		}
		return actionThrow;
	}

	private JMenu getMenuHighScore() {
		if(menuHighScore == null) {
			menuHighScore = new JMenu();
			menuHighScore.setText("High Score");
			menuHighScore.add(getButtonViewHighScore());
		}
		return menuHighScore;
	}

	private JMenuItem getButtonViewHighScore() {
		if(buttonViewHighScore == null) {
			buttonViewHighScore = new JMenuItem();
			buttonViewHighScore.setText("View High Score");
			buttonViewHighScore.setAction(getHighScoreButtonAction());
		}
		return buttonViewHighScore;
	}

	HighScoreView hsv = null;
	@SuppressWarnings("serial")
	private AbstractAction getHighScoreButtonAction() {
		if(highScoreButtonAction == null) {
			highScoreButtonAction = new AbstractAction("View High Score", null) {
				public void actionPerformed(ActionEvent evt) {
					if(hsv == null)
						hsv = new HighScoreView();
					else
						hsv.setVisible(true);
				}
			};
		}
		return highScoreButtonAction;
	}

	private JLabel getLabelTime() {
		if(labelTime == null) {
			labelTime = new JLabel();
			labelTime.setText("Time Left:");
			labelTime.setBounds(190, 11, 53, 16);
			labelTime.setFont(new java.awt.Font("Tahoma",0,10));
		}
		return labelTime;
	}

	private JMenuItem getRulesButton() {
		if(rulesButton == null) {
			rulesButton = new JMenuItem();
			rulesButton.setText("Rules");
			rulesButton.setAction(getRulesButtonAction());
			rulesButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
		}
		return rulesButton;
	}

	private JMenuItem getPlayerButton() {
		if(playerButton == null) {
			playerButton = new JMenuItem();
			playerButton.setText("Player");
			playerButton.setAction(getPlayerButtonAction());
			playerButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.ALT_MASK));
		}
		return playerButton;
	}
	Rules tempRules = null;
	@SuppressWarnings("serial")
	private AbstractAction getRulesButtonAction() {
		if(rulesButtonAction == null) {
			rulesButtonAction = new AbstractAction("Rules", null) {
				public void actionPerformed(ActionEvent evt) {
					if(tempRules == null) 
						tempRules = new Rules();
					else
						tempRules.setVisible(true);
				}
			};
		}
		return rulesButtonAction;
	}
	@SuppressWarnings("serial")
	private AbstractAction getPlayerButtonAction() {
		if(playerButtonAction == null) {
			playerButtonAction = new AbstractAction("Player", null) {
				public void actionPerformed(ActionEvent evt) {
					pSettings.repaint();
					pSettings.setVisible(true);
				}
			};
		}
		return playerButtonAction;
	}

	private JTextField getTxtTime() {
		if(txtTime == null) {
			txtTime = new JTextField();
			txtTime.setText("Default");
			txtTime.setBounds(245, 7, 39, 21);
			txtTime.setEditable(false);
			txtTime.setBackground(Color.white);
			txtTime.setSize(45, 21);
		}
		return txtTime;
	}
	private JLabel getLblDie() {
		if(lblDie == null) {
			lblDie = new JLabel();
			lblDie.setText("Die:");
			lblDie.setBounds(471, 9, 23, 17);
			lblDie.setFont(new java.awt.Font("Tahoma",0,11));
		}
		return lblDie;
	}

	private JTextField getTxtDie() {
		if(txtDie == null) {
			txtDie = new JTextField();
			txtDie.setBounds(492, 8, 39, 20);
			txtDie.setText("Default");
			txtDie.setEditable(false);
			txtDie.setBackground(Color.white);
			txtDie.setSize(45, 20);
		}
		return txtDie;
	}

	private JLabel getLblTurn() {
		if(lblTurn == null) {
			lblTurn = new JLabel();
			lblTurn.setText("Turn:");
			lblTurn.setBounds(324, 10, 26, 15);
			lblTurn.setOpaque(true);
			lblTurn.setFont(new java.awt.Font("Tahoma",0,10));
			lblTurn.setBackground(new java.awt.Color(255,255,255));
		}
		return lblTurn;
	}

	private JTextField getTxtTurn() {
		if(txtTurn == null) {
			txtTurn = new JTextField();
			txtTurn.setBounds(350, 8, 60, 20);
			txtTurn.setMargin(new Insets(0,0,0,0));
			txtTurn.setText("Default");
			txtTurn.setEditable(false);
			txtTurn.setBackground(Color.white);
		}
		return txtTurn;
	}

	private JPanel getPanelP1() {
		if(panelP1 == null) {
			panelP1 = new StartBox(Color.red, "Red", 1);
			panelP1.setBounds(55, 40, 150, 120);
			panelP1.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		}
		return panelP1;
	}

	private JPanel getPanelP2() {
		if(panelP2 == null) {
			panelP2 = new StartBox(Color.blue, "Blue", 2);
			panelP2.setBounds(550, 40, 150, 120);
			panelP2.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		}
		return panelP2;
	}

	private JPanel getPanelP4() {
		if(panelP4 == null) {
			panelP4 = new StartBox(Color.yellow, "Yellow", 4);
			panelP4.setBounds(55, 420, 150, 120);
			panelP4.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		}
		return panelP4;
	}

	private JPanel getPanelP3() {
		if(panelP3 == null) {
			panelP3 = new StartBox(Color.green, "Green", 3);
			panelP3.setBounds(550, 420, 150, 120);
			panelP3.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		}
		return panelP3;
	}

	private JLabel getLblP1() {
		if(lblP1 == null) {
			lblP1 = new JLabel();
			lblP1.setText("Default");
			lblP1.setBounds(80, 20, 60, 15);
		}
		return lblP1;
	}

	private JLabel getLblP2() {
		if(lblP2 == null) {
			lblP2 = new JLabel();
			lblP2.setText("Default");
			lblP2.setBounds(575, 20, 60, 15);
		}
		return lblP2;
	}

	private JLabel getLblP4() {
		if(lblP4 == null) {
			lblP4 = new JLabel();
			lblP4.setText("Default");
			lblP4.setBounds(80, 400, 60, 15);
		}
		return lblP4;
	}

	private JLabel getLblP3() {
		if(lblP3 == null) {
			lblP3 = new JLabel();
			lblP3.setText("Default");
			lblP3.setBounds(575, 400, 60, 15);
		}
		return lblP3;
	}

	//-------------View part 2--------------------------------
	Square[] squares = new Square[40];

	/**
	 * We failed to find a good algorithm for this.
	 * @see Constants.GUI.GUI.Constants for explanations of the variables
	 */
	private void initSquares(){
		int x = Constants.GUI.playingfield_firstSquareX, y = Constants.GUI.playingfield_firstSquareY;

		//Phase one, top left section.
		for(int k = 1; k < 6; k++){
			Square s = new Square(k);
			this.getContentPane().add(s);
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
			squares[k] = s;
			x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		}
		x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		for(int l = 6; l < 10; l++){
			y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
			Square s = new Square(l);
			this.add(s);
			squares[l] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}
		for(int i = 10; i < 11; i++){ //To bypass duplicate variable "s" problem.
			x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
			Square s = new Square(i);
			this.add(s);
			squares[i] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}

		//Phase two, top right section
		x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		for(int k = 11; k < 16; k++){
			Square s = new Square(k);
			this.getContentPane().add(s);
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
			squares[k] = s;
			y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
		}
		x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
		for(int l = 16; l < 20; l++){
			Square s = new Square(l);
			this.add(s);
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
			squares[l] = s;
			x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		}
		x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		for(int i = 20; i < 21; i++){
			y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
			Square s = new Square(i);
			this.add(s);
			squares[i] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}

		//Phase three, bottom right section
		y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
		for(int k = 21; k < 26; k++){
			Square s = new Square(k);
			this.getContentPane().add(s);
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
			squares[k] = s;
			x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		}
		x += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		for(int l = 26; l < 30; l++){
			y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
			Square s = new Square(l);
			this.add(s);
			squares[l] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}
		for(int i = 30; i < 31; i++){
			x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
			Square s = new Square(i);
			this.add(s);
			squares[i] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}

		//Phase four, bottom left section
		x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
		for(int k = 31; k < 36; k++){
			Square s = new Square(k);
			this.getContentPane().add(s);
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
			squares[k] = s;
			y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
		}
		y += Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
		for(int l = 36; l < 40; l++){
			x -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingX;
			Square s = new Square(l);
			this.add(s);
			squares[l] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}
		for(int i = 0; i < 1; i++){
			y -= Constants.GUI.squareSide + Constants.GUI.playingfield_spacingY;
			Square s = new Square(i);
			this.add(s);
			getContentPane().add(getLabelTime());
			squares[i] = s;
			s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);
		}

		//Fix the start squares
		squares[1].setColor(Color.red);
		squares[11].setColor(Color.blue);
		squares[21].setColor(Color.green);
		squares[31].setColor(Color.yellow);	

		addHomeRows();
	}

	private Square[][] homerows = new Square[4][4];

	private void addHomeRows(){

		int k = 40; //-> 55
		for(int i = 0; i < 4; i++){
			int next = i*10;
			int x = squares[next].getX();
			int y = squares[next].getY();
			for(int j = 0; j < 4; j++){
				x += (i==0)?Constants.GUI.squareSide+Constants.GUI.playingfield_spacingX:((i==2)?(-Constants.GUI.squareSide-Constants.GUI.playingfield_spacingX):0);
				y += (i==1)?(Constants.GUI.squareSide+Constants.GUI.playingfield_spacingY):((i==3)?(-Constants.GUI.squareSide-Constants.GUI.playingfield_spacingY):0);
				Square s = new Square(k);
				this.add(s);
				s.setBounds(x, y, Constants.GUI.squareSide, Constants.GUI.squareSide);	
				s.setColor(squares[(i*10)+1].getColor());
				homerows[i][j] = s; //indexing
				k++;
			}
		}
	}

	NewGameView ngv = null;
	@SuppressWarnings("serial")
	private AbstractAction getAbstractNewGame() {
		if(abstractNewGame == null) {
			abstractNewGame = new AbstractAction("New Game", null) {
				public void actionPerformed(ActionEvent evt) {
					if(ngv == null)
						ngv = new NewGameView();
					else
						ngv.setVisible(true);
				}
			};
		}
		return abstractNewGame;
	}

	private final View v = this;

	@SuppressWarnings("serial")
	private AbstractAction getActionSave() {
		if(actionSave == null) {
			actionSave = new AbstractAction("Save", null) {
				public void actionPerformed(ActionEvent evt) {
					JFileChooser fc = new JFileChooser();
					int returnVal = fc.showSaveDialog(v);

					if(returnVal == JFileChooser.APPROVE_OPTION) {
						EventBus.publish(Events.SAVE_GAME.toString(), fc.getSelectedFile().getAbsolutePath());
						System.out.println("You chose to save this file: " +
								fc.getSelectedFile().getAbsolutePath());
					}
				}
			};
		}
		return actionSave;
	}

	@SuppressWarnings("serial")
	private AbstractAction getActionLoad() {
		if(actionLoad == null) {
			actionLoad = new AbstractAction("Load", null) {
				public void actionPerformed(ActionEvent evt) {
					JFileChooser fc = new JFileChooser();
 
					FileFilter filter1 = new ExtensionFileFilter("file", new String[] { "ludo", "file" });
					fc.setFileFilter(filter1);

					int returnVal = fc.showOpenDialog(v);
					if(returnVal == JFileChooser.APPROVE_OPTION) {
						EventBus.publish(Events.LOAD_GAME.toString(), fc.getSelectedFile().getAbsolutePath());
						System.out.println("You chose to load this file: " +
								fc.getSelectedFile().getAbsolutePath());
					}
				}
			};
		}
		return actionLoad;
	}

	/**
	 * This handler specifies what happens every time the Model publishes something to
	 * the EventBus. The plan is that View should extract the new locations of player
	 * pieces from this, and simply move them accordingly.
	 * 
	 * More granularity can be added for more specific events.
	 */

	private void reset(){
		for(StartBox s: startBoxes)
			s.reset();
		for(Square s:squares)
			this.remove(s);
		for(int i = 0; i < homerows.length; i++){
			for(int j = 0; j < homerows.length; j++){
				this.remove(homerows[i][j]);
			}
		}
		squares = new Square[40];
		homerows = new Square[4][4];
		initSquares();
		repaint();
	}


	@EventTopicSubscriber(topic="ERROR")
	public void onError(String topic, String message){
		JOptionPane.showMessageDialog(this, message);
		repaint();
	}

	@EventTopicSubscriber(topic="WIN")
	public void onWin(String topic, String player){
		int  d = JOptionPane.showConfirmDialog(null, player + " is the winner! do you want to start a new game?");
		if(d == 0){
			new NewGameView();
		}
		else{
			exitButton.doClick();
		}
		repaint();
	}

	@EventTopicSubscriber(topic="DICETHROWUPDATE")
	public void onDiceUpdate(String topic, Integer result){
		txtDie.setText(result.toString());
		btnDie.setDieValue(result);
		btnDie.animate(); 
	}

	@EventTopicSubscriber(topic="TIME")
	public void onTime(String topic, Integer i){
		this.txtTime.setText(i.toString());
		repaint();
	}

	private void die(Integer value){
		Integer dieFace = (Integer) value;
		this.txtDie.setText(dieFace.toString());
		if (value > -1){
			btnDie.setDieValue(value);
		}
		repaint();
	}


	@EventTopicSubscriber(topic="UPDATE")
	public void onUpdate(String topic, IModel model) {

		reset();

		/*
		 * Set a black boarder round the player names  
		 */
		lblP1.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
		lblP2.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
		lblP3.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
		lblP4.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
		Integer current = model.getState().getCurrentPlayer();

		/*
		 * Set a red color for the current player in game 
		 */

		if(current == 0)
			lblP1.setBorder(new LineBorder(Color.red, 1, false));
		if(current == 1)
			lblP2.setBorder(new LineBorder(Color.red, 1, false));
		if(current == 2)
			lblP3.setBorder(new LineBorder(Color.red, 1, false));	
		if(current == 3)
			lblP4.setBorder(new LineBorder(Color.red, 1, false));	

		/*
		 * Just go through the players currently in game, pull out their names, color etc,
		 * and update this information in the view.
		 */

		//dim1 = player (1-4)
		//dim2 = piece index [4]

		JLabel[] pnames = {lblP1, lblP2, lblP3, lblP4};
		playerNames = pnames;

		die(model.getState().getCurrentDieValue());
		for(int i = 0; i < model.getState().getNumOfPlayers(); i++){

			if(model.getState().getPlayer(i) != null)
				pnames[i].setText(model.getState().getPlayer(i).getPlayerName());
			else
				pnames[i].setText("");
		}

		txtTurn.setText(model.getState().getPlayer(model.getState().getCurrentPlayer()).toString().split(" ")[0]);

		Integer[][] pieces = model.getPlayingField().getPieceLocations();

		for(int i = 0; i < pieces.length; i++){
			for(int j = 0; j < pieces.length; j++){
				System.out.println("Player "+i+", piece "+j+": "+pieces[i][j]);
				Integer p = pieces[i][j];
				if(p != null){
					if(p >= 0 && p < 40){
						squares[p].addPiece(new Piece(model.getState().getPlayer(i).getPlayerColor(), p));
					}
					if(p > 39 && p < 56)
						homerows[i][j%4].addPiece(new Piece(model.getState().getPlayer(i).getPlayerColor(), p));
					if(p > 55 && p < 60)
						panelP1.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p), p%56);
					if(p > 59 && p < 64)
						panelP2.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p),p%60);
					if(p > 63 && p < 68)
						panelP3.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p),p%64);
					if(p > 67 && p < 72)
						panelP4.specificAdd(new Piece(model.getState().getPlayer(i).getPlayerColor(), p),p%68);
					System.out.println("Pieces: " +p);
				}
			}
		}
		
		/***************************
		 * Change the color of the field corresponding to the players color 
		 */

		if(model.getState().getPlayer(0) != null)
			squares[1].setColor(model.getState().getPlayer(0).getPlayerColor());
		if(model.getState().getPlayer(1) != null)
			squares[11].setColor(model.getState().getPlayer(1).getPlayerColor());
		if(model.getState().getPlayer(2) != null)
			squares[21].setColor(model.getState().getPlayer(2).getPlayerColor());
		if(model.getState().getPlayer(3) != null)
			squares[31].setColor(model.getState().getPlayer(3).getPlayerColor());

		for(int i = 0; i < 4; i++){
			if(model.getState().getPlayer(i) != null)
				homerows[i][0].setColor(model.getState().getPlayer(i).getPlayerColor());
			if(model.getState().getPlayer(i) != null)
				homerows[i][1].setColor(model.getState().getPlayer(i).getPlayerColor());
			if(model.getState().getPlayer(i) != null)
				homerows[i][2].setColor(model.getState().getPlayer(i).getPlayerColor());
			if(model.getState().getPlayer(i) != null)
				homerows[i][3].setColor(model.getState().getPlayer(i).getPlayerColor());
			if(model.getState().getPlayer(i) != null)
				startBoxes[i].setColor(model.getState().getPlayer(i).getPlayerColor());
			
			//End of the repaint of the color corresponding to each player. 
		}
		repaint();

		System.out.println("View: UPDATE done");
	}	
}